It's my first time with curses module and I want make menu with this.
I have found example on internet for make this.
import curses
from curses import panel
class Menu(object):
def __init__(self, items, stdscreen):
self.window = stdscreen.subwin(0, 0)
self.window.keypad(1)
self.panel = panel.new_panel(self.window)
self.panel.hide()
panel.update_panels()
self.position = 0
self.items = items
self.items.append(("exit", "exit"))
def navigate(self, n):
self.position += n
if self.position < 0:
self.position = 0
elif self.position >= len(self.items):
self.position = len(self.items) - 1
def display(self):
self.panel.top()
self.panel.show()
self.window.clear()
while True:
self.window.refresh()
curses.doupdate()
for index, item in enumerate(self.items):
if index == self.position:
mode = curses.A_REVERSE
else:
mode = curses.A_NORMAL
msg = "%d. %s" % (index, item[0])
self.window.addstr(10 + index, 1, msg, mode)
key = self.window.getch()
if key in [curses.KEY_ENTER, ord("\n")]:
if self.position == len(self.items) - 1:
break
else:
self.items[self.position][1]()
elif key == curses.KEY_UP:
self.navigate(-1)
elif key == curses.KEY_DOWN:
self.navigate(1)
self.window.clear()
self.panel.hide()
panel.update_panels()
curses.doupdate()
class MyApp(object):
def __init__(self, stdscreen):
self.screen = stdscreen
curses.curs_set(0)
submenu_items = [("beep", curses.beep), ("flash", curses.flash)]
submenu = Menu(submenu_items, self.screen)
main_menu_items = [
("beep", curses.beep),
("flash", curses.flash),
("submenu", submenu.display),
]
main_menu = Menu(main_menu_items, self.screen)
main_menu.display()
if __name__ == "__main__":
curses.wrapper(MyApp)
It's work pretty good, but I want show a Banner for my title. For this I use rich module and pyfiglet like this :
from rich import print
from rich.text import Text
from rich.panel import Panel
banner = Text(pyfiglet.figlet_format("Title Test", font="bulbhead"), style="bold magenta")
bannerPanel = Panel(Text(pyfiglet.figlet_format("Title Test", font="bulbhead"), style="bold magenta").plain)
I can't add Text object or Panel object with self.window.addstr() because he want a string object.
Is it possible to show bannerPanel or banner above my menu?
Thanks in advance
The only thing I managed to do is add Text().plain.
Related
Nodes of the same level can be dragged and prevented from entering each other, not other parent nodes.
I overwrote the dragMoveEvent and dropEvent methods in the QTreeWidget.I only have the second layer effect correct, which can drag each other and prevent entry into each other. But the first and third levels have different errors. For example, node1-1-1 and Node1-2-2 cannot be dragged to each other. Node1 can be dragged inside Node2, which is not in accordance with my finished requirements.
class TreeWidget(QTreeWidget):
def __init__(self):
super().__init__()
self.setDragDropMode(QTreeWidget.InternalMove)
def dragMoveEvent(self, event):
current_item = self.currentItem()
item = self.itemAt(event.pos())
if current_item and current_item.type() == 0:
super().dragMoveEvent(event)
elif item and item.type() == 1 and current_item.parent() == item.parent():
super().dragMoveEvent(event)
elif item and item.type() == 2 and current_item.parent() == item.parent():
super().dragMoveEvent(event)
else:
event.ignore()
def dropEvent(self, event):
current_item = self.currentItem()
item = self.itemAt(event.pos())
if current_item and current_item.type() == 0:
super(TreeWidget, self).dropEvent(event)
elif item and item.type() == 1 and current_item.parent() == item.parent():
super(TreeWidget, self).dropEvent(event)
elif item and item.type() == 2 and current_item.parent() == item.parent():
super(TreeWidget, self).dropEvent(event)
else:
event.ignore()
class BasicTreeTest1(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('QTreeWidget')
self.tree = TreeWidget()
root1 = QTreeWidgetItem(self.tree,type=0)
root1.setText(0, 'node1')
child1_1 = QTreeWidgetItem(root1,type=1)
child1_1.setText(0, 'node1-1')
child1_1.setFlags(child1_1.flags() & ~Qt.ItemIsDropEnabled)
child1_2 = QTreeWidgetItem(root1, type=1)
child1_2.setText(0, 'node1-2')
child1_2.setFlags(child1_2.flags() & ~Qt.ItemIsDropEnabled)
child1_1_1 = QTreeWidgetItem(child1_1, type=2)
child1_1_1.setText(0, 'node1-1-1')
child1_1_1.setFlags(child1_1_1.flags() & ~Qt.ItemIsDropEnabled)
child1_2_1 = QTreeWidgetItem(child1_1, type=2)
child1_2_1.setText(0, 'node1-2-1')
child1_2_1.setFlags(child1_2_1.flags() & ~Qt.ItemIsDropEnabled)
root2 = QTreeWidgetItem(self.tree,type=0)
root2.setText(0, 'node2')
child2_1 = QTreeWidgetItem(root2, type=1)
child2_1.setText(0, 'node2-1')
child2_1.setFlags(child2_1.flags() & ~Qt.ItemIsDropEnabled)
child2_2 = QTreeWidgetItem(root2, type=1)
child2_2.setText(0, 'node2-2')
child2_2.setFlags(child2_2.flags() & ~Qt.ItemIsDropEnabled)
root3 = QTreeWidgetItem(self.tree, type=0)
root3.setText(0, 'node3')
child3_1 = QTreeWidgetItem(root3, type=1)
child3_1.setText(0, 'node3_1')
child3_1.setFlags(child3_1.flags() & ~Qt.ItemIsDropEnabled)
child3_2 = QTreeWidgetItem(root3, type=1)
child3_2.setText(0, 'node3_2')
child3_2.setFlags(child3_2.flags() & ~Qt.ItemIsDropEnabled)
child3_2_1 = QTreeWidgetItem(child3_2, type=2)
child3_2_1.setText(0, 'node3-2-1')
child3_2_1.setFlags(child3_2_1.flags() & ~Qt.ItemIsDropEnabled)
child3_2_2 = QTreeWidgetItem(child3_2, type=2)
child3_2_2.setText(0, 'node3-2-2')
child3_2_2.setFlags(child3_2_2.flags() & ~Qt.ItemIsDropEnabled)
# root1.setFlags(root1.flags() & ~Qt.ItemIsDropEnabled)
# root2.setFlags(root2.flags() & ~Qt.ItemIsDropEnabled)
# root3.setFlags(root3.flags() & ~Qt.ItemIsDropEnabled)
# child22.setFlags(child22.flags() & ~Qt.ItemIsDropEnabled)
self.setCentralWidget(self.tree)
self.tree.expandAll()
if __name__ == '__main__':
app = QApplication(sys.argv)
w = BasicTreeTest1()
w.show()
sys.exit(app.exec_())
This site is my reference: https://blog.csdn.net/this_is_id/article/details/125258736
UPDATE:
Since your latest edits change the requirements, a different approach is needed. At the moment, the only solution I can see is to get the current drop-indicator, which then makes it possible to know if the drop-target is above or below an item. Qt uses a private method for this, which must be ported to PyQt to access the same functionality. I have implemented this in the demo script below, which only allows sibling items (at any level) to be re-ordered within their own parent:
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class TreeWidget(QTreeWidget):
def __init__(self):
super().__init__()
self.setDragDropMode(QTreeWidget.InternalMove)
def dragMoveEvent(self, event):
if self.canDrop(event):
super().dragMoveEvent(event)
else:
event.ignore()
def dropEvent(self, event):
if self.canDrop(event):
super().dropEvent(event)
else:
event.ignore()
def canDrop(self, event):
target = self.itemAt(event.pos())
current = self.currentItem()
if target is not None and target.parent() is current.parent():
index = self.indexFromItem(target)
indicator = self.dragIndicator(
event.pos(), self.visualItemRect(target), index)
return (indicator == QAbstractItemView.AboveItem or
indicator == QAbstractItemView.BelowItem)
return False
def dragIndicator(self, pos, rect, index):
indicator = QAbstractItemView.OnViewport
if not self.dragDropOverwriteMode():
margin = int(max(2, min(rect.height() / 5.5, 12)))
if pos.y() - rect.top() < margin:
indicator = QAbstractItemView.AboveItem
elif rect.bottom() - pos.y() < margin:
indicator = QAbstractItemView.BelowItem
elif rect.contains(pos, True):
indicator = QAbstractItemView.OnItem
else:
touching = rect.adjust(-1, -1, 1, 1)
if touching.contains(pos, False):
indicator = QAbstractItemView.OnItem
if (indicator == QAbstractItemView.OnItem and
not self.model().flags(index) & Qt.ItemIsDropEnabled):
if pos.y() < rect.center().y():
indicator = QAbstractItemView.AboveItem
else:
indicator = QAbstractItemView.BelowItem
return indicator
class BasicTreeTest1(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('QTreeWidget')
self.tree = TreeWidget()
root1 = QTreeWidgetItem(self.tree,type=0)
root1.setText(0, 'node1')
child1_1 = QTreeWidgetItem(root1,type=1)
child1_1.setText(0, 'node1-1')
child1_2 = QTreeWidgetItem(root1, type=1)
child1_2.setText(0, 'node1-2')
child1_1_1 = QTreeWidgetItem(child1_1, type=2)
child1_1_1.setText(0, 'node1-1-1')
child1_2_1 = QTreeWidgetItem(child1_1, type=2)
child1_2_1.setText(0, 'node1-2-1')
root2 = QTreeWidgetItem(self.tree,type=0)
root2.setText(0, 'node2')
child2_1 = QTreeWidgetItem(root2, type=1)
child2_1.setText(0, 'node2-1')
child2_2 = QTreeWidgetItem(root2, type=1)
child2_2.setText(0, 'node2-2')
root3 = QTreeWidgetItem(self.tree, type=0)
root3.setText(0, 'node3')
child3_1 = QTreeWidgetItem(root3, type=1)
child3_1.setText(0, 'node3_1')
child3_2 = QTreeWidgetItem(root3, type=1)
child3_2.setText(0, 'node3_2')
child3_2_1 = QTreeWidgetItem(child3_2, type=2)
child3_2_1.setText(0, 'node3-2-1')
child3_2_2 = QTreeWidgetItem(child3_2, type=2)
child3_2_2.setText(0, 'node3-2-2')
self.setCentralWidget(self.tree)
self.tree.expandAll()
if __name__ == '__main__':
app = QApplication(sys.argv)
w = BasicTreeTest1()
w.setGeometry(600, 100, 500, 400)
w.show()
sys.exit(app.exec_())
PREVIOUS SOLUTION:
To prevent child items dropping onto each other you can change their item-flags. The default flags include Qt.ItemIsDropEnabled, so you just need to remove that:
child111 = QTreeWidgetItem(root1, type=1)
child111.setFlags(child111.flags() & ~Qt.ItemIsDropEnabled)
Of course, this won't stop items being dragged and dropped between parents, but your current code already prevents that by reimplementing dragMoveEvent and dropEvent, so it looks like the above changes are all that's needed.
Here is a complete working example based on your code:
from PyQt5 import QtCore, QtWidgets
class TreeWidget(QtWidgets.QTreeWidget):
def __init__(self):
super().__init__()
self.setDragDropMode(QtWidgets.QTreeWidget.InternalMove)
def dragMoveEvent(self, event):
current_item = self.currentItem()
item = self.itemAt(event.pos())
if item and item.type() == 1 and current_item.parent() == item.parent():
super().dragMoveEvent(event)
else:
event.ignore()
def dropEvent(self, event):
current_item = self.currentItem()
item = self.itemAt(event.pos())
if item and item.type() == 1 and current_item.parent() == item.parent():
super(TreeWidget, self).dropEvent(event)
else:
event.ignore()
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.tree = TreeWidget()
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.tree)
root1 = QtWidgets.QTreeWidgetItem(self.tree,type=0)
root1.setText(0, '1')
child111 = QtWidgets.QTreeWidgetItem(root1,type=1)
child111.setFlags(child111.flags() & ~QtCore.Qt.ItemIsDropEnabled)
child111.setText(0, '11')
child12 = QtWidgets.QTreeWidgetItem(root1, type=1)
child12.setFlags(child12.flags() & ~QtCore.Qt.ItemIsDropEnabled)
child12.setText(0, '12')
root2 = QtWidgets.QTreeWidgetItem(self.tree,type=0)
root2.setText(0, '2')
child121 = QtWidgets.QTreeWidgetItem(root2, type=1)
child121.setFlags(child121.flags() & ~QtCore.Qt.ItemIsDropEnabled)
child121.setText(0, '21')
child122 = QtWidgets.QTreeWidgetItem(root2, type=1)
child122.setFlags(child122.flags() & ~QtCore.Qt.ItemIsDropEnabled)
child122.setText(0, '22')
self.tree.expandAll()
if __name__ == '__main__':
app = QtWidgets.QApplication(['Test'])
window = Window()
window.setWindowTitle('Test')
window.setGeometry(600, 100, 300, 200)
window.show()
app.exec_()
I'm using PySimpleGUI (version 4.55.1) to make a GUI for a python-based Window application.
There are many screens in this application.
For security reason, I need to implement:
"If there is no actions on GUI for a certain time (e.g, 1min), then the program will automatically back to the top screen"
Is there any way I can achieve that with PysimpleGUI?
The idea only would also help me a lot.
Thanks
There's option auto_close=True in sg.Window, but it doesn't work for multi-window sg.read_all_windows now.
So more complex job to do by counting the duration by yourself. Two ways for it
Count by timeout event in main_window, it make more programming techniques required.
Count by element.Widget.after in sub_window, it make code more clear, but with tkinter code.
Demo code for case 1.
import PySimpleGUI as sg
class Marqueen(sg.Text):
def __init__(self, texts, size=80, font=('Courier New', 11), sep=' ~~~ ',
t=200, fg='white', bg='green'):
self.texts = texts
self.size = size
self.font = font
self.sep = sep
self.t = t
self.running = False
self.index = 0
super().__init__('', size=size, font=self.font, relief=sg.RELIEF_SUNKEN,
text_color=fg, background_color=bg)
def start(self):
message = self.sep.join(self.texts)
text = message
while len(text) < self.size:
text += self.sep + message
self.text = text + self.sep + text[:self.size]
self.limit = len(text+self.sep)
self.running = True
self.run()
def run(self):
if not self.running:
return
self.update(self.text[self.index:self.index+self.size])
self.update()
self.index += 1
if self.index == self.limit:
self.index = 0
self.Widget.after(self.t, self.run)
def pause(self):
self.running = False
def stop(self):
self.running = False
self.index = 0
def new_window(index, duration=5):
marqueen = Marqueen(news[index])
layout = [[marqueen]]
window = sg.Window(f'News', layout, finalize=True, no_titlebar=True,
location = (450, 50*(index+2)))
window.marqueen = marqueen
window.duration = duration
return window
font = ("Courier New", 16)
sg.theme("DarkGreen1")
sg.set_options(font=font)
index = 0
limit = 5
sub_wins = []
news = [
["France to start vaccinating children aged 5-11"],
["Comet Leonard has been dazzling the night sky in a pre-Christmas show"],
["Plans unveiled for high-tech '10-minute city' in Seoul"],
["France's $19 billion weapons deal is sweet revenge"],
["'Koala massacre' prompts hundreds of cruelty charges"],
]
frame = [[sg.Checkbox(f'Sub Window {i+1}', disabled=True, key=f'Check {i}')] for i in range(limit)]
layout = [[sg.Frame('', frame), sg.Button('New')]]
window = sg.Window("Multi-Window", layout, location=(100, 100), size=(300, 220), finalize=True)
for i in range(limit):
window[f'Check {i}'].Widget.configure(disabledforeground='white')
while True:
win, event, values = sg.read_all_windows(timeout=100)
if event in (sg.WINDOW_CLOSED, 'Exit'):
if win in sub_wins:
win.marqueen.stop()
win.close()
sub_wins.remove(win)
index -= 1
window[f'Check {index}'].update(value=False)
for i, w in enumerate(sub_wins):
w.move(450, 50*(i+2))
else:
break
elif event == 'New':
if index == limit:
continue
w = new_window(index)
w.marqueen.start()
sub_wins.append(w)
window[f'Check {index}'].update(value=True)
index += 1
elif event == sg.TIMEOUT_EVENT and sub_wins:
for w in sub_wins:
w.duration -= 0.1
if w.duration <= 0:
w.write_event_value(None, None)
for win in sub_wins:
win.close()
window.close()
Here's another most-simple demo code for case 2
from random import randint
import PySimpleGUI as sg
def sub_win(duration=5):
text = 'Sub Window'
location = (randint(100, width-100), randint(100, height-100))
layout = [[sg.Text(text)]]
window = sg.Window(text, layout, location=location, finalize=True)
window.TKroot.after(int(duration*1000), lambda win=window:win.write_event_value(sg.WINDOW_CLOSED, None))
return window
width, height = sg.Window.get_screen_size()
layout = [[sg.Button('New Window')]]
window = sg.Window('Main Window', layout, keep_on_top=True, finalize=True)
windows = []
while True:
win, event, values = sg.read_all_windows()
if event in (sg.WINDOW_CLOSED, 'Exit'):
if win in windows:
windows.remove(win)
win.close()
else:
break
elif event == 'New Window':
w = sub_win()
windows.append(w)
for w in windows:
w.close()
window.close()
I am creating a custom qtreewidget class that autoresizes its window to fit exactly the currently visible elements (I don't want to have to scroll). To do this, I run a count function to find the number of open qtreewidgetitems and their children and set a fixed height from there. However, when I expand a child widget (click the expand arrow on one of my items) the whole view suddenly needs to scroll because there is extra white space at the bottom, despite my count function accurately calculating the needed height. How do I get rid of it?
Below is a working class that can be run directly as is.
import sys
from PyQt4 import QtGui
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class TreeWidget(QTreeWidget):
def __init__(self, parent=None):
super(TreeWidget, self).__init__()
self.installEventFilter(self)
self.setStyleSheet('''
background: None;
border: None;
outline: None;
outline-width: 0px;
selection-background-color: blue;
''')
header = QtGui.QTreeWidgetItem(["Tree", "First"])
self.setAutoScroll(False)
self.setHeaderItem(header)
self.header().close()
# self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
def fill_item(self, item, value):
item.setExpanded(False)
if type(value) is dict:
for key, val in sorted(value.iteritems()):
child = QTreeWidgetItem()
child.setText(0, unicode(key))
# child.doubleClicked.connect(lambda: self.doubleClicked1(child))
item.addChild(child)
self.fill_item(child, val)
elif type(value) is list:
last = None
for val in value:
child = QTreeWidgetItem()
if type(val) is dict:
item.addChild(child)
child.setText(0, '[dict]')
# child.doubleClicked.connect(lambda: self.doubleClicked1(child))
self.fill_item(child, val)
last = child
elif type(val) is list:
self.fill_item(last, val)
else:
item.addChild(child)
child.setText(0, unicode(val))
child.setText(1, 'test')
# child.doubleClicked.connect(lambda: self.doubleClicked1(child))
child.setExpanded(False)
last = child
else:
child = QTreeWidgetItem()
child.setText(0, unicode(val))
child.setText(1, 'test')
# child.doubleClicked.connect(lambda: self.doubleClicked1(child))
item.addChild(child)
def fill_widget(self, value):
self.clear()
self.fill_item(self.invisibleRootItem(), value)
def resizeEvent(self, event):
self.resize()
def resize(self):
width = 50
self.header().resizeSection(1, width)
self.header().resizeSection(0, self.width()-width)
height = self.visibleCount()
print height/15
self.setFixedHeight(height+0)
def eventFilter(self, source, event):
if source is self:
if event.type() == 1:
self.resize()
elif event.type() == QEvent.Leave:
self.clearSelection()
return QtGui.QTreeWidget.eventFilter(self, source, event)
def visibleCount(self, parent=0):
height = 0
if parent == 0:
topHeight = 0
for a in xrange(self.topLevelItemCount()):
item = self.topLevelItem(a)
topHeight += self.visualItemRect(item).height()
if item.isExpanded():
height += self.visibleCount(item)
height += topHeight
else:
childHeight = 0
for a in xrange(parent.childCount()):
item = parent.child(a)
childHeight += self.visualItemRect(item).height()
if item.isExpanded():
height += self.visibleCount(item)
height += childHeight
return height
def editClicked(self, parent=0):
# print 'edit 2'
if parent == 0:
for a in xrange(self.topLevelItemCount()):
item = self.topLevelItem(a)
print type(item)
item.setExpanded(True)
self.editClicked(item)
else:
for a in xrange(parent.childCount()):
item = parent.child(a)
print type(item)
item.setText(1, '+')
item.setExpanded(True)
self.editClicked(item)
def doubleClicked1(self, widget):
print widget
def main():
app = QtGui.QApplication(sys.argv)
ex = TreeWidget()
data = [
'Make sure ZMQ remote button gets honored',
'Fill in plot',
'Remove cycle',
'Remove current control or make it working',
'Handle possible startup errors with dialogs',
'Get improved current read-out (requires hardware changes)',
['1','2','3'],
'Email quench notification'
]
ex.fill_widget(data)
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You can remove the event filter entirely. You need to resize the widget when the items are expanded or collapsed. Both actions trigger signals, so you can simply connect those signals to resize:
class TreeWidget(QTreeWidget):
def __init__(self, parent=None):
super(TreeWidget, self).__init__(parent)
self.expanded.connect(self.resize)
self.collapsed.connect(self.resize)
[edit]
It seems I solved the problem... In fact, I now do that:
class Gameboard(QGraphicsScene):
def deletePawn(self, num):
pawnToDelete = self.pawns.pop(num)
pawnToDelete.delete()
class Pawn(QGraphicsItem):
def delete(self):
child.prepareGemotryChange()
child.setParent(None)
#idem for each child
self.gameboard.removeItem(self)
self.gameboard = None
[/edit]
What is the good way to implement references in a pyqt QGraphicsScene?
I made a class Gameboard which inherits QGraphicsScene, and this gameboard contains a variable number of pawns (wich inherit QGraphicsPolygonItem)
Pawns can be created or deleted dynamically, creation is ok but deletion sometimes crash...
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4 import QtOpenGL
class Gameboard(QGraphicsScene):
def __init__(self):
super(Gameboard, self).__init__()
self.pawns = {}
self.currentPawn = None
def createSquares(self):
#create the polygons of the squares and affect their coordinates x,y
def createPawn(self):
pawn = Pawn(self)
num = len(self.pawns)
self.pawns[num] = pawn
self.addItem(self.pawns[num])
def deletePawn(self, num):
self.currentPawn = None
self.pawns[num].beforeDelete()
self.pawns[num].prepareGeometryChange()
self.removeItem(self.pawns[num])
del self.pawns[num]
def selectPawn(self, pawn):
#a pawn is selected, by click for example
self.currentPawn = pawn
def keyPressEvent(self, event):
ekey = event.key()
if ekey == Qt.Key_Delete and self.currentPawn != None:
num = self.currentPawn.number
self.deletePawn(num)
class Pawn(QGraphicsItem):
def __init__(self, gameboard):
super(Pawn, self).__init__()
self.gameboard = gameboard
self.number = 0
self.pos = (-1,-1)
self.name = ""
def create(self, x, y, num):
#create a QGraphicsPolygonItem, a QGraphixPixmapItem and a QGraphicsTextItem which are child of the QGraphicsItem Pawn, and set position
self.number = num
self.pos = (x,y)
self.gameboard.squares[(x,y)].occupiedBy = self
def move(self, newPos):
self.gameboard.squares[self.pos].occupiedBy = None
self.pos = newPos
self.gameboard.squares[self.pos].occupiedBy = None
def beforeDelete(self):
#a function I add trying to get rid of the crash
self.gameboard = None
self.graphicsPolygon.setParent = None
self.graphicsPix.setParent = None
self.text.setParent = None
def mousePressEvent(self, event):
super(Pawn, self).mousePressEvent(event)
if event.button() == 1:
self.gameboard.currentPawn = self
event.accept()
class Square(QGraphicsPolygonItem):
def __init__(self, gameboard):
self.coordinates = (x,y)
self.occupiedBy = None
What is the proper way to proceed, should I use deleteLater?
Or maybe something with the weakref lib?
Is it because of the variable gameboard in Pawn?
I have a wxListCtrl that display a table with information (with rows and column fields). Typically the row will be highlighted only when you click it with the mouse button. But I would like to have the highlight without clicking it. i.e. When I move the mouse to different row, the row will be highlighted without clicking the mouse. Is this possible?
########################################################################
import wx
import sys, glob
class MyForm(wx.Frame):
#----------------------------------------------------------------------
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "List Control Tutorial")
# Add a panel so it looks the correct on all platforms
panel = wx.Panel(self, wx.ID_ANY)
self.index = 0
self.list_ctrl = wx.ListCtrl(panel, size=(-1,100),
style=wx.LC_REPORT
|wx.BORDER_SUNKEN
)
self.list_ctrl.InsertColumn(0, 'Subject')
self.list_ctrl.InsertColumn(1, 'Due')
self.list_ctrl.InsertColumn(2, 'Location', width=125)
self.list_ctrl.Bind(wx.EVT_ENTER_WINDOW, self.onMouseOver)
self.list_ctrl.Bind(wx.EVT_LEAVE_WINDOW, self.onMouseLeave)
btn = wx.Button(panel, label="Add Line")
btn.Bind(wx.EVT_BUTTON, self.add_line)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self.list_ctrl, 0, wx.ALL|wx.EXPAND, 5)
sizer.Add(btn, 0, wx.ALL|wx.CENTER, 5)
panel.SetSizer(sizer)
bmp = wx.Image("icon.bmp", wx.BITMAP_TYPE_BMP).ConvertToBitmap()
il = wx.ImageList(16,16)
il.Add(bmp)
self.list_ctrl.AssignImageList(il,wx.IMAGE_LIST_SMALL)
line = "Line %s" % self.index
self.list_ctrl.InsertStringItem(self.index, line,-1)
self.list_ctrl.SetStringItem(self.index, 1, "01/19/2010")
self.list_ctrl.SetStringItem(self.index, 2, "USA")
self.index += 1
self.list_ctrl.InsertStringItem(self.index, line,-1)
self.list_ctrl.SetStringItem(self.index, 1, "01/19/2010")
self.list_ctrl.SetStringItem(self.index, 2, "USA")
#self.list_ctrl.SetItemBackgroundColour(self.index,wx.LIGHT_GREY)
self.index += 1
self.list_ctrl.InsertStringItem(self.index, line,-1)
self.list_ctrl.SetStringItem(self.index, 1, "01/19/2010")
self.list_ctrl.SetStringItem(self.index, 2, "USA")
self.index += 1
#----------------------------------------------------------------------
def add_line(self, event):
if self.index > 0:
image = 1
else:
image = -1
line = "Line %s" % self.index
self.list_ctrl.InsertStringItem(self.index, line,image)
self.list_ctrl.SetStringItem(self.index, 1, "01/19/2010")
self.list_ctrl.SetStringItem(self.index, 2, "USA")
self.index += 1
def onMouseOver(self, event):
print "mouse over"
for item in range(self.list_ctrl.GetItemCount()):
self.list_ctrl.SetItemBackgroundColour(item,wx.NullColor)
x = event.GetX()
y = event.GetY()
item, flags = self.list_ctrl.HitTest((x, y))
self.list_ctrl.SetItemBackgroundColour(item,wx.RED)
#self.list_ctrl.RefreshItems(0,2)
event.Skip()
def onMouseLeave(self, event):
print "mouse leave"
for item in range(self.list_ctrl.GetItemCount()):
self.list_ctrl.SetItemBackgroundColour(item,wx.NullColor)
#self.list_ctrl.RefreshItems(0,2)
event.Skip()
'''
def onMouseOver(self, event): #USED to display tooltip on items that cannot be selected
x = event.GetX()
y = event.GetY()
item, flags = self.list_ctrl.HitTest((x, y))
color = self.list_ctrl.GetItemBackgroundColour(item)
if color == wx.NullColor:
self.list_ctrl.SetItemBackgroundColour(item,wx.RED)
elif color == wx.RED:
item = item - 1
color = self.list_ctrl.GetItemBackgroundColour(item)
self.list_ctrl.SetItemBackgroundColour(item,wx.Nu)
'''
#----------------------------------------------------------------------
# Run the program
if __name__ == "__main__":
app = wx.App(False)
frame = MyForm()
frame.Show()
app.MainLoop()
I would try grabbing the background color of one of the ListItems when you create the ListCtrl and putting it in a variable:
self.defaultItemColor = someListItem.GetBackgroundColour()
Then use that to change the color back. After you call the item's setter, you sometimes need to call the ListCtrl's SetItem(listItem) method as well. For some reason, setting the background to NullColour doesn't work with the ListCtrl. I discovered that back when I was creating a dark mode for one of my applications. I actually wrote about it here: http://www.blog.pythonlibrary.org/2011/11/05/wxpython-creating-a-dark-mode/
I know that this is a late response but the following works for me using:
self.listCtrl.Bind(wx.EVT_MOTION, self.onMouseOver)
and setting self.previous_item initially to -1
I am using it to highlight the row with the mouse over it and alter the tooltip at the same time.
def onMouseOver(self, event):
x = event.GetX()
y = event.GetY()
self.item, flags = self.listCtrl.HitTest((x, y))
if self.item < 0:
self.listCtrl.SetToolTipString("Colour codes Red - Loaded, Yellow - In Progress, Green - Finished, Blue - Invoiced, White - User defined")
return
if self.item != self.previous_item:
self.old_item = self.previous_item
self.previous_item = self.item
else:
return
bg_colour = self.listCtrl.GetItemBackgroundColour(self.item)
if bg_colour == wx.BLACK or bg_colour == wx.NullColour:
self.listCtrl.SetItemBackgroundColour(self.item,"#3246A8")
self.listCtrl.SetItemBackgroundColour(self.old_item,wx.BLACK)
elif bg_colour == "#3246A8":
self.listCtrl.SetItemBackgroundColour(self.item,wx.BLACK)
self.currentItem = self.item
rowid = self.listCtrl.GetItem(self.currentItem,13)
stat_test = rowid.GetText()
rowid = self.listCtrl.GetItem(self.currentItem,1)
file_test = rowid.GetText()
rowid = self.listCtrl.GetItem(self.currentItem,4)
running_test = rowid.GetText()
if stat_test == "0":
self.listCtrl.SetToolTipString("File currently playing\nRunning time "+running_test)
elif stat_test == "1":
self.listCtrl.SetToolTipString("In Progress\nRunning time "+running_test)
elif stat_test == "2":
self.listCtrl.SetToolTipString("Finished\nRunning time "+running_test)
elif stat_test == "3":
self.listCtrl.SetToolTipString("Invoiced\nRunning time "+running_test)
if file_test == self.file_playing and stat_test == "1":
self.listCtrl.SetToolTipString("File currently playing & In Progress\nRunning time "+running_test)
if file_test == self.file_playing and stat_test == "2":
self.listCtrl.SetToolTipString("File currently playing but Finished\nRunning time "+running_test)
if file_test == self.file_playing and stat_test == "3":
self.listCtrl.SetToolTipString("File currently playing but Invoiced\nRunning time "+running_test)
I Hope it helps someone
This is not "easily" done out of the box, but you should be able to do it with a bit of tinkering.
You'll have to add a mouseover and mouseout event listener to your wxListCtrl object, and then test which item is being hit each time you get a mouseover event. You may be able to cache the coordinates of the list items, but scrolling lists and window resizes could pose a problem if you go this route.
Some code to get you started (not tested, probably won't work and you'd need to add a MyListCtrl to a suitable wx.Frame):
import wx
class MyListCtrl(wx.ListCtrl):
def __init__(self, parent, id):
wx.ListCtrl.__init__(self, parent, id)
self.Bind(wx.EVT_ENTER_WINDOW, self.onMouseOver)
self.Bind(wx.EVT_LEAVE_WINDOW, self.onMouseLeave)
def onMouseOver(self, event):
#Loop through all items and set bgcolor to default, then:
item = self.HitTest(event.GetPosition())
self.SetItemBackgroundColour(item, 'Green')
self.RefreshItems()
event.Skip()
def onMouseLeave(self, event):
#Loop through all items and set bgcolor to default, then:
self.RefreshItems()
event.Skip()