I have three widgets under my main window . I design my program that each widget will be controlled by a class .
Last 2 hours I waste my time to fit 3 GUI elements to one of this widgets.
I want to have a label and a line item and a button. I put them in to a grid layout(in real case I have 3 row for simplicity I put 1 row in the example below). And set the grid layout's parent to my class.
What I expect is this GUI items to resize themselves and fit to one row in this widget, no matter what I tried I couldn't succeed .
In short I expect this items to shrink to fit to the widget by themselves. But couldn't figure out how to do it. Any advice ?
void
enviromentSetup::createDialogs()
{
numberOfPoints = new QLabel (QApplication::translate("leftPanel","numberOfPoints"));
inputNumberOfPoints = new
QLineEdit(QString::number(st_environmentParamaters.number_of_points_in_line));
maxElementsButton = new QPushButton("Max Elems");
gridLayout = new QGridLayout(this);
gridLayout->addWidget(numberOfPoints, 0, 0);
gridLayout->addWidget(inputNumberOfPoints, 0, 1);
gridLayout->addWidget(maxElementsButton, 0, 2);
this->show();
}
It is insufficient to merely make the parent of the layout the main widget. You also have to explicitly set the layout of the widget.
Add the following line just before you show the widget:
this->setLayout(gridLayout);
Related
I have created a GUI with the aid of Tkinter that has created a 3 [C]olomn by 8 [R]ow, (I have placed the borders of the 'cells' in red) and output is like so:
Just to calrify:
Where the "logo [E]" is (C1,R1) & (C1,R2) are rowspanned and where the "main window" is from (C2,R3) to (C3,R8) are colomnspanned and rowspanned.
All are Label widget except for (C1,R3 to C1,R8), which are button widgets with a command to display a string in "Main Window" (gray area).
My aim is to make the "Main Window"(gray area) display only in different grid layouts based on the selected button on the left. So for example I would like to make the grey have a grid layout of 2col by 4 row when C1,R6 is pressed, or when C1,R3 is pressed the grid layout to be 2Cx6R all in the gray area.
Q: Is there a way of re-gridding a label? If so how? Or an alternative approach?
ADDENDUM:
Ideally I would like to update the GREEN area with different number of row and column configurations. For example: When Micro Audit is touched, the Green area will configure itself to have 4 rows by 2 columns, and when Find Item is selected, it will only 3 rows with 1 column, etc. :
In my project, I have 256 tiny PushButtons in one 16x16 grid layout. (Yeah that took forever.) Editing and running my program now is very laggy. Also, for some strange reason, Qt will not let me enable any of the buttons, but other buttons to the side work just fine?
Is there any easy way to determine which square of the grid was clicked without having a bunch of buttons? (Like following the cursor over an image maybe?)
Also, when each "square" of the grid is clicked, it becomes the "selection" and it needs to be the only "square" selected. (Think about it like a huge chess board)
Here is a pic: http://gyazo.com/988cdbb59b3d1f1873c41bf91b1408fd
Later on, I will need to do this again for a 54x54 size grid (2916 buttons) and I REALLY don't want to do it button by button.
Thanks for your time, I hope you understand my question :)
You can do this easy way, I've explained almost everything in code, but if you have any questions about it feel free to ask, and please, accept this answer if it solved your problem :)
import sys
from PyQt4 import QtGui, QtCore
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class DrawImage(QMainWindow):
def __init__(self, parent=None):
super(QMainWindow, self).__init__(parent)
self.setWindowTitle('Select Window')
#you can set grid size here ... 8x8, 16x16 , for bigger numbers (54x54) be sure your image is big enough, because QWidget can't be smaller then ~20 pixels
self.gridSize = 16
mainWidget = QWidget()
self.setCentralWidget(mainWidget)
self.scene = QGraphicsScene()
view = QGraphicsView(self.scene)
layout = QVBoxLayout()
layout.addWidget(view)
mainWidget.setLayout(layout)
self.image = QImage('image.JPG')# put your image name here, image (suppose to be grid) must be at the same folder or put full path
pixmapItem = QGraphicsPixmapItem(QPixmap(self.image), None, self.scene)
pixmapItem.mousePressEvent = self.pixelSelect
def pixelSelect( self, event ):
#add whatever you want to this widget,any functionality or you can add image for example, I've simply colored it
wdg = QWidget()
layout = QVBoxLayout()
palette = QPalette(wdg.palette())
palette.setBrush(QPalette.Background, QColor(200,255,255))
wdg.setPalette(palette)
wdg.setLayout(layout)
self.scene.addWidget(wdg)
#calculate size and position for added widget
imageSize = self.image.size()
width = imageSize.width()
height = imageSize.height()
#size
wgWidth = float(width)/self.gridSize
wgHeight = float(height)/self.gridSize
wdg.setFixedSize(wgWidth,wgHeight)
#position
wgXpos = int(event.pos().x()/wgWidth) * wgWidth
wgYpos = int(event.pos().y()/wgHeight) * wgHeight
wdg.move(wgXpos, wgYpos)
#which square is clicked?
print "square at row ", int(event.pos().y()/wgHeight)+1,", column ",int(event.pos().x()/wgWidth)+1, "is clicked"
def main():
app = QtGui.QApplication(sys.argv)
form = DrawImage()
form.show()
app.exec_()
if __name__ == '__main__':
main()
Also, if you want to display simple square image over your grid image, look at question/solution I had: QGraphicsPixmapItem won't show over the other QGraphicsPixmapItem
If you don't really need the appearance of buttons, I would create a QWidget subclass that implements a custom paintEvent and renders a grid of needed size (taking the widget size into account). Then, in the mouse events (up,down,move etc.) you can calculate which grid item was clicked with a simple formula. You can render cells with different colors to indicate selection or highlighting.
P.S.:I would really like to post some code from my implementations (i have done this two or three times) but the source codes are at my old company :)
You just create your own QGridLayout in order to be able to add the buttons easily.
I posted an answer to another question, showing you how to fill a custom made QGridLayout with a bunch of widgets sequentially. The buttons are added according to the maximum count of columns you specified. (Note: It's just a very rough example but enough to start from)
In your example you would create the custom grid layout with 16 columns and simply add your buttons.
To find out which button has been pressed (and to make connecting easier) you can use QSignalMapper.
For investigating the lag you could check the amount of (GDI-/User-) handles of your application (using ProcessExplorer for example). The handle count shouldn't be above 10.000.
I don't know why you can't enable the push buttons.
This is my UI
I hope I could move red border to left or right in the picture by mouse when the program run.
How should I do?
Add all those widgets to the form in the UI designer. Select text edit widget and list widget. Select "Lay Out Horizontally in Splitter" from the top toolbar. Then select the form itself and select "Lay Out Vertically" from the top toolbar. That's it, except the list widget is not dockable.
Edit:
If you want to keep the list widget at constant size (so that it is resized only by the user), tell the QSplitter the stretch factors of the widgets, for example like this:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
// index 0 is the text edit, stretch factor is 1 (= all stretching goes here)
ui->splitter->setStretchFactor(0, 1);
// index 1 is the list widget, stretch factor is 0
ui->splitter->setStretchFactor(1, 0);
}
I have two images--img1 and img2--and I would like to be able to compare both images. I would like to overlay them over each other with a slider which would let me see more of img1 or img2. Lastly, when I move in one image I would also like to move in the other img. This is what I have so far.
QGraphicsScene *scn = new QGraphicsScene( this );
ui->view->setScene( scn );
QPixmap *im = new QPixmap("P3C.jpg");
QPixmap *i = new QPixmap("result.jpg");
scn->addPixmap( *im );
scn->addPixmap(*i);
Use QGraphicsItemGroup to group the 2 pixmap items so they will act as one. Set the item group to ItemIsMovable so you can move them.
Set the opacity of the item that is on top of the other so the bottom one can show thru. You can connect the value change signal of your slider widget to your object's slot to control the opacity.
I'm still getting used to the sizers in wxWidgets, and as such can't seem to make them do what I want.
I want a large panel that will contain a list of other panels/boxes, which each then contain a set of text fields
----------------------
| label text box |
| label2 text box2 |
----------------------
----------------------
| label text box |
| label2 text box2 |
----------------------
----------------------
| label text box |
| label2 text box2 |
----------------------
I also need to be able to add (at the end), and remove(anywhere) these boxes.
If there's too many to fit in the containing panel a vertical scroll bar is also required.
This is what I've tried so far, it works for the first box that's created with the containing panel, but additional added items are just a small box in the top left of the main panel, even though the sizer code is the same for all boxes.
//itemsList is a container containg a list of *Item pointers to each box/panel/whatever the right name is
Items::Items(wxWindow *parent)
:wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN)
{
//one sstarting item
OnAdd(wxCommandEvent());
}
void Items::OnAdd(wxCommandEvent &event)
{
unsigned id = itemsList .size();
Item *item = new Item(this,id);
itemsList .push_back(item);
RebuildSizer();
}
void Items::RebuildSizer()
{
this->SetSizer(0,true);
wxBoxSizer *sizerV = new wxBoxSizer(wxVERTICAL);
for(std::vector<Item*>::iterator it = itemsList .begin(); it != itemsList .end(); ++it)
sizerV->Add(*it, 1, wxEXPAND | wxLEFT | wxRIGHT, 5);
SetSizer(sizerV);
}
void Items::OnRemove (wxCommandEvent &event, unsigned itemId)
{
delete itemsList [itemId];
itemsList .erase(items.begin()+itemId);
for(std::vector<Item*>::iterator it = itemsList .begin()+itemId; it != itemsList .end(); ++it)
(*it)->ChangeId(itemId++);
RebuildSizer();
}
Also what's the best way to lay out the contents of each box? I was thinking of using a 2 by 2 grid sizer but I'm not sure how to make the text boxes to expand to be as wide as possible while making the labels stay as small as possible (but also maintaining the alignment between the 2 text boxes)?
"If theres too many to fit in the containing panel a vertical scroll bar is also required."
You could have a look at wxScrolledWindow.
"additional added items are just a small box in the top left of the main panel"
I am not sure, but, maybe a call to wxSizer::Layout() will help.
"Also whats the best way to lay out the contents of each box?"
Have a look at this sizerdemo. If it is not mandatory, that the labels stay as small as possible, you could give the labels a fixed width and only let the text boxes grow. If you want to adapt the size when adding or removing new boxes, you could implement the OnSize() event handler.
May I suggest to you to post this question to one of the wxWidgets mailing list? They will be able to help you very quickly.
Can I also recommend the wxForum, I have found it very useful for wxWidgets questions in the past.
More specifically for scrolling wxScrolledWindow should help, use wxScrolledWindow->SetSizer with your top level sizer (the one that contains your controls) to set up a scrolled area, also check out the samples called scroll, scrollsub and vscroll included in wxWidgets (in the samples directory of your wxwidgets install).