I've written this piece of code with intention of making something similar to Excel, where you can scroll up and down in a spreadsheet. I have found, however, that when you scroll back up, that it leaves the lowest cell instead of scrolling it. I can't seem to figure out where I went wrong, any help is appreciated.
from tkinter import *
def scroll(event): #changes from what element to start drawing
global starting_value
starting_value -= event.delta/120
draw_list(starting_value)
def draw_list(starting_value): #draws entries from specified index
for index,i in enumerate(list_of_numbers[starting_value:starting_value+10]):
i.grid(row=index)
list_of_numbers = [Entry() for i in range(100)]
for index,i in enumerate(list_of_numbers):
i.insert(0,index)
i.bind('<MouseWheel>',scroll)
starting_value = 0
draw_list(starting_value)
mainloop()
Every time you scroll, any items that are no longer supposed to be visible are left in their original grid positions: you have perhaps placed new items at the same row/column, but that doesn't actually remove the original items from the grid. You'd need something like this at the top of draw_list() to explicitly un-grid the old items:
for widget in container.grid_slaves():
widget.grid_forget()
Where container is the window or Frame containing your scrolling items - your example code does not actually give this object a name, you would need to do something like container = Tk() so that you can refer to it.
Related
I have added tooltips to my checkbox elements, but they are too annoying. They appear immediately after hovering the mouse cursor over the element and do not disappear after the cursor has left the checkbox.
I could start a timer, but I don't know how I can check if the cursor is within the desired element or has left it.
And the second question is, is there any event like wxEVT_LEAVE_WINDOW, but for the checkbox to remove the tooltip when the cursor goes out of bounds.
Thanks, #New Pagodi
I am still dont get normal behaivor of tips, but your trich works. I can get tip window from wxCheckBox element just calling GetChildren().
wxRichToolTip* tip = new wxRichToolTip(wxT("INFO"), msg);
tip->SetTimeout(0, 500);
tip->ShowFor(it->second.first);
wxWindowList tipWindow = it->second.first->GetChildren();
auto a = tipWindow.GetLast()->GetData();
a->Bind(wxEVT_MOTION, &DeviceListDialog::onLeaveCheckbox, this);
I am working on a Python application that plots data from a large file containing records from lots and lots of sources. One of the options I am trying to give the user is the option to only plot for a subset of these sources if so desired. I accomplish this by first reading the files, finding out how many unique things there are, and then creating a QCheckBox() for each, named after its source (each source has a unique name). In this particular case, the data file is parsed into a giant dictionary where the keys are the unique source. I want to connect to the stateChange() event for each checkbox and then disable plotting for that source when the box is unchecked. Which in this case would be adding/removing the source from a list of sources when the box is checked/unchecked. The problem I am running into is that all of my checkboxes end up connecting to the final source in my list.
Initially, the window that is created looks correct, each button is named appropriately. Every time a button gets pressed, the btnstate() is supposed to simply print the text associated with that button. The method works if you can explicitly define each button, as shown by the radio buttons in the example. If you click either, you will get the correct name of the button printed, but when unchecking/rechecking ANY of the check boxes, btnstate prints "test4".
What am I doing wrong?
Here is the code (sources changed to dummy values):
import sys
from PyQt4.QtGui import *
def btnstate(b):
print b.text()
def main():
app = QApplication([])
widget = QWidget()
layout = QVBoxLayout()
widget.setLayout(layout)
radio_layout = QHBoxLayout()
checkbox_layout = QHBoxLayout()
#setup radio buttons for config pop-up window
r1 = QRadioButton("Page Count")
r2 = QRadioButton("Date")
r1.toggled.connect(lambda:btnstate(r1))
r2.toggled.connect(lambda:btnstate(r2))
radio_layout.addWidget(r1)
radio_layout.addWidget(r2)
cbs = []
for idx, serial in enumerate(["test1", "test2", "test3", "test4"]):
temp = QCheckBox(serial)
temp.setText(serial)
temp.setChecked(True)
checkbox_layout.addWidget(temp)
temp.stateChanged.connect(lambda:btnstate(temp))
cbs.append(temp)
layout.addLayout(radio_layout)
layout.addLayout(checkbox_layout)
widget.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I think the reason that this is happening has to do with how Python binds and unbinds references inside the loop. Because temp is being redefined on each iteration, the slot is also being updated, so that it effectively calls the same lambda for every button. That's sort of hand-wavy, because my understanding of the details of Python referencing is not super deep. But I know that the Python bindings to Qt have a lot of problems with Python's referencing and garbage collection, e.g. when deleting widgets, because the Qt object hierarchy doesn't totally work with Python.
Anyway, more practically, there is a pretty easy fix. You can use the functools.partial method to define a partial function as the slot, rather than a lambda. Bind the button as the first object, leaving the button state (emitted as the signal argument) unbound. Like so:
import functools
def btnstate(button, state):
print button.text()
Then in the loop:
for idx, serial in enumerate(['test1', 'test2', 'test3', 'test4']):
temp = QCheckBox(serial)
checkbox_layout.addWidget(temp)
temp.stateChanged.connect(functools.partial(btnstate, serial))
Running this, I now get the correct labels printed when each box is check/unchecked.
Edit:
See this post for another example of Python's reference counting interacting with strange ways with Qt's object hierarchy.
app=Tk()
age=IntVar()
name=StringVar()
id=IntVar()
def add_user():
app1=Tk()
L1 = Message(app1,text="Name")
L1.pack( side = LEFT)
E1 = Entry(app1,textvariable=name)
E1.pack()
L2 = Message(app1,text="\nAge")
L2.pack( side = LEFT)
E2 = Spinbox(app1,from_=1,to_=100,textvariable=age)
E2.pack()
l3=Message(app1,text="\nId")
l3.pack()
e3=Spinbox(app1,from_=1,to_=100,textvariable=id)
e3.pack()
b5=Button(app1,text="submit",command=app1.destroy)
b5.pack()
app1.mainloop()
print age.get(),name.get(),id.get()
return
b1=Button(app,command=add_user,relief=RIDGE,text="add patient details")
b1.pack(side=BOTTOM)
app.mainloop()
the print statement doesn't print the correct values,it always prints the default values.I don't understand where I made a mistake
The reason you can't get the values is that the widgets have been destroyed once mainloop has exited.
The bigger problem in your code is that you are creating two instances of Tk. Tkinter isn't designed to work that way. A proper Tkinter program creates exactly one instance of Tk, and exits when that one instance is destroyed. If you want to create more than one window, the second and subsequent windows need to be instances of Toplevel.
You might find the answer to this question useful: Correct way to implement a custom popup tkinter dialog box
I'm using a QTreeView with a QStandardItemModel and I'm trying to figure out how to move items up and down the tree using buttons . I can do drag and drop no problem, but what I would like to do is have some buttons associated with "move up" and "move down" functions. I just cant find anything on the subject. There seems to be a "moveRow()" function for the model object, but I cant find any documentation on it so I'm not sure if its what I need. Any information you could give to point me in the right direction would be greatly appreciated!
PS Here are my QT Creator stats:
Qt Creator 2.6.2
Based on Qt 5.0.1 (64 bit)
Your hunch is correct. moveRow() is the right function to call.
To move items within one parent (it's a tree, after all), you'd do moveRow(parent, index.row(), parent, index.row() + delta), where delta is set to 1 or -1 depending on whether you move down or up, respectively.
If you want to allow items to be moved between parents, you'll need additional logic to figure out the destination parent if the item would be moved past its parent.
Do note that it's considered bad design if the move button are separate from the items to be moved. Your delegate should display up and down arrows for each item, in its row, so that you can move things with one click. When there is a contiguous selection, the delegates should merge the up/down arrows to cover all of the items. When the selection is non-contiguous, the up/down arrows should disappear.
With separate buttons, you need two clicks: first select the item, then click up/down. This sucks from user experience point of view.
moveRow doesn't seem to work in QTreeView.
Here's a simple (PyQt5) solution for moving an item "up one" relative to its sibling(s). It must have at least one sibling "above". Also this is only for moving between siblings, not where you want to move a row to a different parent. However, I think that these issues could be engineered without difficulty on the basis of this code.
One thing to be aware of here is that the QStandardItem doesn't change in operations like this, but that the associated QModelIndex does.
# in almost all QTreeView implementations the "tree" structure is at column 0...
curr_index = self.selectionModel().currentIndex().siblingAtColumn(0)
if curr_index.isValid():
curr_item = self.model().itemFromIndex(curr_index)
curr_row = curr_index.row()
# if this is the top sibling of its parent it cant be moved up...
if curr_row > 0:
parent_item = self.model().itemFromIndex(curr_index.parent())
# NB parent_item is None in the case of root children:
# in that case you therefore use "takeRow" and "insertRow" methods of the model, not the item!
take_row = self.model().takeRow if parent_item == None else parent_item.takeRow
insert_row = self.model().insertRow if parent_item == None else parent_item.insertRow
row_to_move = take_row(curr_row)
insert_row(curr_row - 1, row_to_move)
new_index = self.model().indexFromItem(curr_item)
# now set the (single) selection, and set current, back to the moved item
# (if you are implementing single-selection, obviously)
flag = QtCore.QItemSelectionModel.SelectionFlag
self.selectionModel().setCurrentIndex(new_index, flag.Clear | flag.SelectCurrent)
This should be simple it seems but I can't quite get it to work. I want a control (I guess CListBox or CListCtrl) which displays text strings in a nice tabulated way.
As items are added, they should be added along a row until that row is full, and then start a new row. Like typing in your wordprocessor - when the line is full, items start being added to the next line, and the control can scroll vertically.
What I get when trying with a list-mode CListCtrl is a single row which just keeps growing, with a horizontal scroll bar. I can't see a way to change that, there must be one?
You probably need a list control wth LVS_REPORT. If you expect the user to add items interactively using a keyboard, you probably need a data grid, not a list. Adding editing to list control subitems is not easy, and it would be easier to start from CWnd. Search "MFC Data Grid" to find some open source class libraries that implemented the feature.
If you can afford adding /clr to your program, you can try the data grid classes in Windows Forms using MFC's Windows Form hosting support. You will find a lot more programming resources on data grid classes in Windows Forms than any other third-party MFC data grid class library.
If you use CRichEditCtrl you can set it to word-wrap, take a look at this snippet extracted from:
http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.ui/2004-03/0111.html
(I've derived my own QRichEditCtrl from the MFC CRichEditCtrl,
and here's the relevant code:)
void QRichEditCtrl::SetWordWrap(bool bWrap)
{
RECT r;
GetWindowRect(&r);
CDC * pDC = GetDC();
long lLineWidth = 9999999; // This is the non-wrap width
if (bWrap)
{
lLineWidth = ::MulDiv(pDC->GetDeviceCaps(PHYSICALWIDTH),
1440, pDC->GetDeviceCaps(LOGPIXELSX));
}
SetTargetDevice(*GetDC(), lLineWidth);
}