KMenu.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. import os
  2. import logging
  3. from PySide6.QtCore import QRect, QSize, Qt, QRectF, QPoint, QEvent, Property
  4. from PySide6.QtWidgets import QMenu, QStyle, QStyleOptionMenuItem, QStyleOption, QWidget, QApplication
  5. from PySide6.QtWidgets import QToolTip
  6. from PySide6.QtGui import QIcon, QImage, QPainter, QFont, QPalette, QColor, QKeySequence, QAction, QActionGroup
  7. import Kikka
  8. from Kikka.KikkaConst import *
  9. from Kikka.Utils.Singleton import singleton
  10. class KMenu(QMenu):
  11. def __init__(self, title='', parent=None):
  12. QMenu.__init__(self, title, parent)
  13. self._parent = parent
  14. self._aRect = {}
  15. self._bg_image = None
  16. self._fg_image = None
  17. self._side_image = None
  18. self._hover_color = None
  19. self.installEventFilter(self)
  20. self.setMouseTracking(True)
  21. self.setSeparatorsCollapsible(False)
  22. def getHoverColor(self):
  23. return self._hover_color
  24. def setHoverColor(self, color):
  25. if isinstance(color, QColor):
  26. self._hover_color = color
  27. hoverColor = Property(QColor, fget=getHoverColor, fset=setHoverColor)
  28. def addMenuItem(self, text, callbackfunc=None, iconfilepath=None, group=None):
  29. if iconfilepath is None:
  30. act = QAction(text, self._parent)
  31. elif os.path.exists(iconfilepath):
  32. act = QAction(QIcon(iconfilepath), text, self._parent)
  33. else:
  34. logging.info("fail to add menu item")
  35. return
  36. if callbackfunc is not None:
  37. act.triggered.connect(callbackfunc)
  38. if group is None:
  39. self.addAction(act)
  40. else:
  41. self.addAction(group.addAction(act))
  42. return act
  43. def addSubMenu(self, menu):
  44. act = self.addMenu(menu)
  45. return act
  46. def getSubMenu(self, menuName):
  47. for i in range(len(self.actions())):
  48. act = self.actions()[i]
  49. if act.text() == menuName:
  50. return act.menu()
  51. return None
  52. def getAction(self, actionName):
  53. for i in range(len(self.actions())):
  54. act = self.actions()[i]
  55. if act.text() == actionName:
  56. return act
  57. return None
  58. def getActionByData(self, data):
  59. for i in range(len(self.actions())):
  60. act = self.actions()[i]
  61. if act.data() == data:
  62. return act
  63. return None
  64. def checkAction(self, actionName, isChecked):
  65. for i in range(len(self.actions())):
  66. act = self.actions()[i]
  67. if act.text() != actionName:
  68. continue
  69. act.setChecked(isChecked)
  70. pass
  71. def setPosition(self, pos):
  72. rect = QApplication.instance().primaryScreen().geometry()
  73. w = rect.width()
  74. h = rect.height()
  75. if pos.y() + self.height() > h: pos.setY(h - self.height())
  76. if pos.y() < 0: pos.setY(0)
  77. if pos.x() + self.width() > w: pos.setX(w - self.width())
  78. if pos.x() < 0: pos.setX(0)
  79. self.move(pos)
  80. def updateActionRect(self):
  81. """
  82. void QMenuPrivate::updateActionRects(const QRect &screen) const
  83. https://cep.xray.aps.anl.gov/software/qt4-x11-4.8.6-browser/da/d61/class_q_menu_private.html#acf93cda3ebe88b1234dc519c5f1b0f5d
  84. """
  85. self._aRect = {}
  86. topmargin = 0
  87. leftmargin = 0
  88. rightmargin = 0
  89. # qmenu.cpp Line 259:
  90. # init
  91. max_column_width = 0
  92. dh = self.height()
  93. y = 0
  94. style = self.style()
  95. opt = QStyleOption()
  96. opt.initFrom(self)
  97. hmargin = style.pixelMetric(QStyle.PixelMetric.PM_MenuHMargin, opt, self)
  98. vmargin = style.pixelMetric(QStyle.PixelMetric.PM_MenuVMargin, opt, self)
  99. icone = style.pixelMetric(QStyle.PixelMetric.PM_SmallIconSize, opt, self)
  100. fw = style.pixelMetric(QStyle.PixelMetric.PM_MenuPanelWidth, opt, self)
  101. deskFw = style.pixelMetric(QStyle.PixelMetric.PM_MenuDesktopFrameWidth, opt, self)
  102. tearoffHeight = style.pixelMetric(QStyle.PixelMetric.PM_MenuTearoffHeight, opt, self) if self.isTearOffEnabled() else 0
  103. # for compatibility now - will have to refactor this away
  104. tabWidth = 0
  105. maxIconWidth = 0
  106. hasCheckableItems = False
  107. # ncols = 1
  108. # sloppyAction = 0
  109. for i in range(len(self.actions())):
  110. act = self.actions()[i]
  111. if act.isSeparator() or act.isVisible() is False:
  112. continue
  113. # ..and some members
  114. hasCheckableItems |= act.isCheckable()
  115. ic = act.icon()
  116. if ic.isNull() is False:
  117. maxIconWidth = max(maxIconWidth, icone + 4)
  118. # qmenu.cpp Line 291:
  119. # calculate size
  120. qfm = self.fontMetrics()
  121. previousWasSeparator = True # this is true to allow removing the leading separators
  122. for i in range(len(self.actions())):
  123. act = self.actions()[i]
  124. if act.isVisible() is False \
  125. or (self.separatorsCollapsible() and previousWasSeparator and act.isSeparator()):
  126. # we continue, this action will get an empty QRect
  127. self._aRect[i] = QRect()
  128. continue
  129. previousWasSeparator = act.isSeparator()
  130. # let the style modify the above size..
  131. opt = QStyleOptionMenuItem()
  132. self.initStyleOption(opt, act)
  133. fm = opt.fontMetrics
  134. sz = QSize()
  135. # sz = self.sizeHint().expandedTo(self.minimumSize()).expandedTo(self.minimumSizeHint()).boundedTo(self.maximumSize())
  136. # calc what I think the size is..
  137. if act.isSeparator():
  138. sz = QSize(2, 2)
  139. else:
  140. s = act.text()
  141. if '\t' in s:
  142. t = s.index('\t')
  143. act.setText(s[t + 1:])
  144. tabWidth = max(int(tabWidth), qfm.boundingRect(s[t + 1:]).width())
  145. else:
  146. seq = act.shortcut()
  147. if seq.isEmpty() is False:
  148. tabWidth = max(int(tabWidth), qfm.boundingRect(seq.toString()).width())
  149. sz.setWidth(fm.boundingRect(QRect(), Qt.TextFlag.TextSingleLine | Qt.TextFlag.TextShowMnemonic, s).width())
  150. sz.setHeight(fm.height())
  151. if not act.icon().isNull():
  152. is_sz = QSize(icone, icone)
  153. if is_sz.height() > sz.height():
  154. sz.setHeight(is_sz.height())
  155. sz = style.sizeFromContents(QStyle.ContentsType.CT_MenuItem, opt, sz, self)
  156. if sz.isEmpty() is False:
  157. max_column_width = max(max_column_width, sz.width())
  158. # wrapping
  159. if y + sz.height() + vmargin > dh - deskFw * 2:
  160. # ncols += 1
  161. y = vmargin
  162. y += sz.height()
  163. # update the item
  164. self._aRect[i] = QRect(0, 0, sz.width(), sz.height())
  165. pass # exit for
  166. max_column_width += tabWidth # finally add in the tab width
  167. sfcMargin = style.sizeFromContents(QStyle.ContentsType.CT_Menu, opt, QSize(0, 0), self).width()
  168. min_column_width = self.minimumWidth() - (sfcMargin + leftmargin + rightmargin + 2 * (fw + hmargin))
  169. max_column_width = max(min_column_width, max_column_width)
  170. # qmenu.cpp Line 259:
  171. # calculate position
  172. base_y = vmargin + fw + topmargin + tearoffHeight
  173. x = hmargin + fw + leftmargin
  174. y = base_y
  175. for i in range(len(self.actions())):
  176. if self._aRect[i].isNull():
  177. continue
  178. if y + self._aRect[i].height() > dh - deskFw * 2:
  179. x += max_column_width + hmargin
  180. y = base_y
  181. self._aRect[i].translate(x, y) # move
  182. self._aRect[i].setWidth(max_column_width) # uniform width
  183. y += self._aRect[i].height()
  184. # update menu size
  185. s = self.sizeHint()
  186. self.resize(s)
  187. def getPenColor(self, opt):
  188. # if opt.menuItemType == QStyleOptionMenuItem.MenuItemType.Separator:
  189. # color = self.separator_color
  190. if opt.state & QStyle.StateFlag.State_Selected and opt.state & QStyle.StateFlag.State_Enabled:
  191. if self._hover_color:
  192. color = self._hover_color
  193. else:
  194. color = opt.palette.color(QPalette.ColorGroup.Active, QPalette.ColorRole.HighlightedText)
  195. elif not (opt.state & QStyle.StateFlag.State_Enabled):
  196. color = opt.palette.color(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text)
  197. else:
  198. color = opt.palette.color(QPalette.ColorGroup.Normal, QPalette.ColorRole.Text)
  199. return color
  200. def drawControl(self, p, opt, arect, icon):
  201. """
  202. due to overrides the "paintEvent" method, so we must repaint all menu item by self.
  203. luckly, we have qt source code to reference.
  204. File: qtbase\src\widgets\styles\qstylesheetstyle.cpp
  205. Function: void drawControl (ControlElement element, const QStyleOption *opt, QPainter *p, const QWidget *w=0) const
  206. """
  207. # Line 3891: case CE_MenuItem:
  208. style = self.style()
  209. p.setPen(self.getPenColor(opt))
  210. # Line 3932: draw icon and checked sign
  211. checkable = opt.checkType != QStyleOptionMenuItem.CheckType.NotCheckable
  212. checked = opt.checked if checkable else False
  213. if opt.icon.isNull() is False: # has custom icon
  214. dis = not (opt.state & QStyle.StateFlag.State_Enabled)
  215. active = opt.state & QStyle.StateFlag.State_Selected
  216. mode = QIcon.Mode.Disabled if dis else QIcon.Mode.Normal
  217. if active != 0 and not dis:
  218. mode = QIcon.Mode.Active
  219. fw = style.pixelMetric(QStyle.PixelMetric.PM_MenuPanelWidth, opt, self)
  220. icone = style.pixelMetric(QStyle.PixelMetric.PM_SmallIconSize, opt, self)
  221. iconRect = QRectF(arect.x() - fw, arect.y(), self._side_image.width(), arect.height())
  222. if checked:
  223. pixmap = icon.pixmap(QSize(icone, icone), mode, QIcon.State.On)
  224. else:
  225. pixmap = icon.pixmap(QSize(icone, icone), mode)
  226. pixw = pixmap.width()
  227. pixh = pixmap.height()
  228. pmr = QRectF(0, 0, pixw, pixh)
  229. pmr.moveCenter(iconRect.center())
  230. if checked:
  231. p.drawRect(QRectF(pmr.x() - 1, pmr.y() - 1, pixw + 2, pixh + 2))
  232. p.drawPixmap(pmr.topLeft(), pixmap)
  233. elif checkable and checked: # draw default checked sign
  234. opt.rect = QRect(0, arect.y(), self._side_image.width(), arect.height())
  235. opt.palette.setColor(QPalette.ColorRole.Text, self.getPenColor(opt))
  236. style.drawPrimitive(QStyle.PrimitiveElement.PE_IndicatorMenuCheckMark, opt, p, self)
  237. # Line 3952: draw menu text
  238. p.setFont(opt.font)
  239. text_flag = Qt.AlignmentFlag.AlignVCenter | Qt.TextFlag.TextShowMnemonic | Qt.TextFlag.TextDontClip | Qt.TextFlag.TextSingleLine
  240. tr = QRect(arect)
  241. s = opt.text
  242. if '\t' in s:
  243. ss = s[s.index('\t') + 1:]
  244. fontwidth = opt.fontMetrics.boundingRect(ss).width()
  245. tr.moveLeft(opt.rect.right() - fontwidth)
  246. tr = QStyle.visualRect(opt.direction, opt.rect, tr)
  247. p.drawText(tr, text_flag, ss)
  248. tr.moveLeft(self._side_image.width() + arect.x())
  249. tr = QStyle.visualRect(opt.direction, opt.rect, tr)
  250. p.drawText(tr, text_flag, s)
  251. # Line 3973: draw sub menu arrow
  252. if opt.menuItemType == QStyleOptionMenuItem.MenuItemType.SubMenu:
  253. arrowW = style.pixelMetric(QStyle.PixelMetric.PM_IndicatorWidth, opt, self)
  254. arrowH = style.pixelMetric(QStyle.PixelMetric.PM_IndicatorHeight, opt, self)
  255. arrowRect = QRect(0, 0, arrowW, arrowH)
  256. arrowRect.moveBottomRight(arect.bottomRight())
  257. arrow = QStyle.PrimitiveElement.PE_IndicatorArrowLeft if opt.direction == Qt.LayoutDirection.RightToLeft else QStyle.PrimitiveElement.PE_IndicatorArrowRight
  258. opt.rect = arrowRect
  259. opt.palette.setColor(QPalette.ColorRole.ButtonText, self.getPenColor(opt))
  260. style.drawPrimitive(arrow, opt, p, self)
  261. pass
  262. def paintEvent(self, event):
  263. # init
  264. self._bg_image = Kikka.style.getMenuBGImage()
  265. self._fg_image = Kikka.style.getMenuFGImage()
  266. self._side_image = Kikka.style.getMenuSideImage()
  267. self.updateActionRect()
  268. p = QPainter(self)
  269. # draw background
  270. p.fillRect(QRect(QPoint(), self.size()), self._side_image.pixelColor(0, 0))
  271. vertical = False
  272. y = self.height()
  273. while y > 0:
  274. yy = y - self._bg_image.height()
  275. p.drawImage(0, yy, self._side_image.mirrored(False, vertical))
  276. x = self._side_image.width()
  277. while x < self.width():
  278. p.drawImage(x, yy, self._bg_image.mirrored(False, vertical))
  279. x += self._bg_image.width()
  280. p.drawImage(x, yy, self._bg_image.mirrored(True, vertical))
  281. x += self._bg_image.width() + 1
  282. y -= self._bg_image.height()
  283. vertical = not vertical
  284. # draw item
  285. actioncount = len(self.actions())
  286. for i in range(actioncount):
  287. act = self.actions()[i]
  288. arect = QRect(self._aRect[i])
  289. if event.rect().intersects(arect) is False:
  290. continue
  291. opt = QStyleOptionMenuItem()
  292. self.initStyleOption(opt, act)
  293. opt.rect = arect
  294. if opt.state & QStyle.StateFlag.State_Selected and opt.state & QStyle.StateFlag.State_Enabled:
  295. # Selected Item, draw foreground image
  296. p.setClipping(True)
  297. p.setClipRect(arect.x() + self._side_image.width(), arect.y(), self.width() - self._side_image.width(),
  298. arect.height())
  299. p.fillRect(QRect(QPoint(), self.size()), self._fg_image.pixelColor(0, 0))
  300. vertical = False
  301. y = self.height()
  302. while y > 0:
  303. x = self._side_image.width()
  304. while x < self.width():
  305. yy = y - self._fg_image.height()
  306. p.drawImage(x, yy, self._fg_image.mirrored(False, vertical))
  307. x += self._fg_image.width()
  308. p.drawImage(x, yy, self._fg_image.mirrored(True, vertical))
  309. x += self._fg_image.width() + 1
  310. y -= self._fg_image.height()
  311. vertical = not vertical
  312. p.setClipping(False)
  313. if opt.menuItemType == QStyleOptionMenuItem.MenuItemType.Separator:
  314. # Separator
  315. # p.setPen(opt.palette.color(QPalette.ColorRole.Text))
  316. y = int(arect.y() + arect.height() / 2)
  317. p.drawLine(self._side_image.width(), y, arect.width(), y)
  318. else:
  319. # MenuItem
  320. self.drawControl(p, opt, arect, act.icon())
  321. pass # exit for
  322. def eventFilter(self, obj, event):
  323. if obj == self:
  324. if event.type() == QEvent.Type.WindowDeactivate:
  325. self.Hide()
  326. elif event.type() == QEvent.Type.ToolTip:
  327. act = self.activeAction()
  328. if act != 0 and act.toolTip() != act.text():
  329. QToolTip.showText(event.globalPos(), act.toolTip())
  330. else:
  331. QToolTip.hideText()
  332. return False
  333. def _createTestMenu(parent=None):
  334. # test callback function
  335. def _test_callback(index=0, title=''):
  336. logging.info("MainMenu_callback: click [%d] %s" % (index, title))
  337. def _test_Exit(testmenu):
  338. # from kikka_app import KikkaApp
  339. callbackfunc = lambda: print("Exit!!")
  340. testmenu.addMenuItem("Exit", callbackfunc)
  341. def _test_MenuItemState(testmenu):
  342. menu = KMenu("MenuItem State", testmenu)
  343. c = 16
  344. for i in range(c):
  345. text = str("%s-item%d" % (menu.title(), i))
  346. callbackfunc = lambda checked, a=i, b=text: _test_callback(a, b)
  347. act = menu.addMenuItem(text, callbackfunc)
  348. if i >= c / 2:
  349. act.setDisabled(True)
  350. act.setText("%s-disable" % act.text())
  351. if i % 8 >= c / 4:
  352. act.setIcon(icon)
  353. act.setText("%s-icon" % act.text())
  354. if i % 4 >= c / 8:
  355. act.setCheckable(True)
  356. act.setText("%s-ckeckable" % act.text())
  357. if i % 2 >= c / 16:
  358. act.setChecked(True)
  359. act.setText("%s-checked" % act.text())
  360. testmenu.addSubMenu(menu)
  361. def _test_Shortcut(testmenu):
  362. menu = KMenu("Shortcut", testmenu)
  363. c = 4
  364. for i in range(c):
  365. text = str("%s-item" % (str(chr(ord('A') + i))))
  366. callbackfunc = lambda checked, a=i, b=text: _test_callback(a, b)
  367. act = menu.addMenuItem(text, callbackfunc)
  368. if i == 0:
  369. act.setShortcut(QKeySequence("Ctrl+T"))
  370. act.setShortcutContext(Qt.ShortcutContext.ApplicationShortcut)
  371. act.setShortcutVisibleInContextMenu(True)
  372. testmenu.addSubMenu(menu)
  373. pass
  374. def _test_StatusTip(testmenu):
  375. pass
  376. def _test_Separator(testmenu):
  377. menu = KMenu("Separator", testmenu)
  378. menu.addSeparator()
  379. c = 5
  380. for i in range(c):
  381. text = str("%s-item%d" % (menu.title(), i))
  382. callbackfunc = lambda checked, a=i, b=text: _test_callback(a, b)
  383. menu.addMenuItem(text, callbackfunc)
  384. for j in range(i + 2): menu.addSeparator()
  385. testmenu.addSubMenu(menu)
  386. def _test_MultipleItem(testmenu):
  387. menu = KMenu("Multiple item", testmenu)
  388. for i in range(100):
  389. text = str("%s-item%d" % (menu.title(), i))
  390. callbackfunc = lambda checked, a=i, b=text: _test_callback(a, b)
  391. menu.addMenuItem(text, callbackfunc)
  392. testmenu.addSubMenu(menu)
  393. def _test_LongTextItem(testmenu):
  394. menu = KMenu("Long text item", testmenu)
  395. for i in range(5):
  396. text = str("%s-item%d " % (menu.title(), i)) * 20
  397. callbackfunc = lambda checked, a=i, b=text: _test_callback(a, b)
  398. menu.addMenuItem(text, callbackfunc)
  399. testmenu.addSubMenu(menu)
  400. def _test_LargeMenu(testmenu):
  401. menu = KMenu("Large menu", testmenu)
  402. for i in range(60):
  403. text = str("%s-item%d " % (menu.title(), i)) * 10
  404. callbackfunc = lambda checked, a=i, b=text: _test_callback(a, b)
  405. menu.addMenuItem(text, callbackfunc)
  406. if i % 5 == 0: menu.addSeparator()
  407. testmenu.addSubMenu(menu)
  408. def _test_LimitTest(testmenu):
  409. menu = KMenu("LimitTest", testmenu)
  410. _test_LargeMenu(menu)
  411. _test_MultipleItem(menu)
  412. _test_LongTextItem(menu)
  413. testmenu.addSubMenu(menu)
  414. def _test_Submenu(testmenu):
  415. menu = KMenu("Submenu", testmenu)
  416. testmenu.addSubMenu(menu)
  417. submenu = KMenu("submenu1", menu)
  418. menu.addSubMenu(submenu)
  419. m = submenu
  420. for i in range(8):
  421. next = KMenu("submenu%d" % (i + 2), testmenu)
  422. m.addSubMenu(next)
  423. m = next
  424. submenu = KMenu("submenu2", menu)
  425. menu.addSubMenu(submenu)
  426. m = submenu
  427. for i in range(8):
  428. for j in range(10):
  429. text = str("%s-item%d" % (m.title(), j))
  430. callbackfunc = lambda checked, a=j, b=text: _test_callback(a, b)
  431. m.addMenuItem(text, callbackfunc)
  432. next = KMenu("submenu%d" % (i + 2), testmenu)
  433. m.addSubMenu(next)
  434. m = next
  435. submenu = KMenu("SubMenu State", testmenu)
  436. c = 16
  437. for i in range(c):
  438. text = str("%s-%d" % (submenu.title(), i))
  439. m = KMenu(text, submenu)
  440. act = submenu.addSubMenu(m)
  441. callbackfunc = lambda checked, a=i, b=text: _test_callback(a, b)
  442. act.triggered.connect(callbackfunc)
  443. if i >= c / 2:
  444. act.setDisabled(True)
  445. act.setText("%s-disable" % act.text())
  446. if i % 8 >= c / 4:
  447. act.setIcon(icon)
  448. act.setText("%s-icon" % act.text())
  449. if i % 4 >= c / 8:
  450. act.setCheckable(True)
  451. act.setText("%s-ckeckable" % act.text())
  452. if i % 2 >= c / 16:
  453. act.setChecked(True)
  454. act.setText("%s-checked" % act.text())
  455. submenu.addSubMenu(m)
  456. menu.addSubMenu(submenu)
  457. def _test_ImageTest(testmenu):
  458. imagetestmenu = KMenu("ImageTest", testmenu)
  459. testmenu.addSubMenu(imagetestmenu)
  460. menu = KMenu("MenuImage-normal", imagetestmenu)
  461. for i in range(32):
  462. text = " " * 54
  463. menu.addMenuItem(text)
  464. imagetestmenu.addSubMenu(menu)
  465. menu = KMenu("MenuImage-bit", imagetestmenu)
  466. menu.addMenuItem('')
  467. imagetestmenu.addSubMenu(menu)
  468. menu = KMenu("MenuImage-small", imagetestmenu)
  469. for i in range(10):
  470. text = " " * 30
  471. menu.addMenuItem(text)
  472. imagetestmenu.addSubMenu(menu)
  473. menu = KMenu("MenuImage-long", imagetestmenu)
  474. for i in range(64):
  475. text = " " * 54
  476. menu.addMenuItem(text)
  477. imagetestmenu.addSubMenu(menu)
  478. menu = KMenu("MenuImage-long2", imagetestmenu)
  479. for i in range(32):
  480. text = " " * 30
  481. menu.addMenuItem(text)
  482. imagetestmenu.addSubMenu(menu)
  483. menu = KMenu("MenuImage-large", imagetestmenu)
  484. for i in range(64):
  485. text = " " * 300
  486. menu.addMenuItem(text)
  487. imagetestmenu.addSubMenu(menu)
  488. menu = KMenu("MenuImage-verylarge", imagetestmenu)
  489. for i in range(100):
  490. text = " " * 600
  491. menu.addMenuItem(text)
  492. imagetestmenu.addSubMenu(menu)
  493. if parent is None:
  494. parent = QWidget(f=Qt.WindowType.Dialog)
  495. icon = QIcon(r"icon.ico")
  496. menu_test = KMenu("TestMenu", parent)
  497. _test_Exit(menu_test)
  498. menu_test.addSeparator()
  499. _test_MenuItemState(menu_test)
  500. _test_Shortcut(menu_test)
  501. _test_StatusTip(menu_test)
  502. _test_Separator(menu_test)
  503. _test_LimitTest(menu_test)
  504. _test_Submenu(menu_test)
  505. menu_test.addSeparator()
  506. _test_ImageTest(menu_test)
  507. menu_test.addSeparator()
  508. _test_Exit(menu_test)
  509. return menu_test
  510. if __name__ == '__main__':
  511. menu = _createTestMenu()
  512. menu.setPosition(QPoint(100, 100))
  513. menu.show()