如何在OptaPlanner的UI中调整颜色?

How to adjust colors in the UI of OptaPlanner?

我目前正在使用 OptaPlanner 的作业调度算法来创建某个计划。我希望计划中使用的每个执行模式都以不同的颜色显示(而不是所有不同的项目都以不同的颜色显示)。是否有可能实现这一点,如果可以,如何实现?我已经搜索代码一段时间了,但不知道该怎么做。

使用作为 OptaPlanner 项目一部分的 Project Scheduling Swing 应用程序无法轻松完成此操作。它使用 JFreeChart 绘制数据,但我找不到将元数据(如颜色)与正在绘制的数据相关联的简单方法。

您可以根据数据项的行 (seriesIndex) 和列(项目在系列中的索引)将 YIntervalRenderer 行为覆盖为您选择的 return 颜色,但您必须保留执行模式和自己[row, column]的映射,比较麻烦

下面是一个修改后的 ProjectJobSchedulingPanel 的例子:

public class ProjectJobSchedulingPanel extends SolutionPanel<Schedule> {

    private static final Logger logger = LoggerFactory.getLogger(ProjectJobSchedulingPanel.class);
    private static final Paint[] PAINT_SEQUENCE = DefaultDrawingSupplier.DEFAULT_PAINT_SEQUENCE;

    public static final String LOGO_PATH = "/org/optaplanner/examples/projectjobscheduling/swingui/projectJobSchedulingLogo.png";

    public ProjectJobSchedulingPanel() {
        setLayout(new BorderLayout());
    }

    @Override
    public void resetPanel(Schedule schedule) {
        removeAll();
        ChartPanel chartPanel = new ChartPanel(createChart(schedule));
        add(chartPanel, BorderLayout.CENTER);
    }

    private JFreeChart createChart(Schedule schedule) {
        YIntervalSeriesCollection seriesCollection = new YIntervalSeriesCollection();
        Map<Project, YIntervalSeries> projectSeriesMap = new LinkedHashMap<>(
                schedule.getProjectList().size());
        ExecutionMode[][] executionModeByRowAndColumn = new ExecutionMode[schedule.getProjectList().size()][schedule.getAllocationList().size()];
        YIntervalRenderer renderer = new YIntervalRenderer() {
            @Override
            public Paint getItemPaint(int row, int column) {
                ExecutionMode executionMode = executionModeByRowAndColumn[row][column];
                logger.info("getItemPaint: ExecutionMode [{},{}]: {}", row, column, executionMode);
                return executionMode == null
                        ? TangoColorFactory.ALUMINIUM_5
                        : PAINT_SEQUENCE[(int) (executionMode.getId() % PAINT_SEQUENCE.length)];
            }
        };
        Map<Project, Integer> seriesIndexByProject = new HashMap<>();
        int maximumEndDate = 0;
        int seriesIndex = 0;
        for (Project project : schedule.getProjectList()) {
            YIntervalSeries projectSeries = new YIntervalSeries(project.getLabel());
            seriesCollection.addSeries(projectSeries);
            projectSeriesMap.put(project, projectSeries);
            renderer.setSeriesShape(seriesIndex, new Rectangle());
            renderer.setSeriesStroke(seriesIndex, new BasicStroke(3.0f));
            seriesIndexByProject.put(project, seriesIndex);
            seriesIndex++;
        }
        for (Allocation allocation : schedule.getAllocationList()) {
            int startDate = allocation.getStartDate();
            int endDate = allocation.getEndDate();
            YIntervalSeries projectSeries = projectSeriesMap.get(allocation.getProject());
            int column = projectSeries.getItemCount();
            executionModeByRowAndColumn[seriesIndexByProject.get(allocation.getProject())][column] = allocation.getExecutionMode();
            logger.info("ExecutionMode [{},{}] = {}", seriesIndexByProject.get(allocation.getProject()), column, allocation.getExecutionMode());
            projectSeries.add(allocation.getId(), (startDate + endDate) / 2.0,
                    startDate, endDate);
            maximumEndDate = Math.max(maximumEndDate, endDate);
        }
        NumberAxis domainAxis = new NumberAxis("Job");
        domainAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        domainAxis.setRange(-0.5, schedule.getAllocationList().size() - 0.5);
        domainAxis.setInverted(true);
        NumberAxis rangeAxis = new NumberAxis("Day (start to end date)");
        rangeAxis.setRange(-0.5, maximumEndDate + 0.5);
        XYPlot plot = new XYPlot(seriesCollection, domainAxis, rangeAxis, renderer);
        plot.setOrientation(PlotOrientation.HORIZONTAL);
        // Uncomment this to use Tango color sequence instead of JFreeChart default sequence.
        // This results in color per project mode.
//        DefaultDrawingSupplier drawingSupplier = new DefaultDrawingSupplier(
//                TangoColorFactory.SEQUENCE_1,
//                DefaultDrawingSupplier.DEFAULT_FILL_PAINT_SEQUENCE,
//                DefaultDrawingSupplier.DEFAULT_OUTLINE_PAINT_SEQUENCE,
//                DefaultDrawingSupplier.DEFAULT_STROKE_SEQUENCE,
//                DefaultDrawingSupplier.DEFAULT_OUTLINE_STROKE_SEQUENCE,
//                DefaultDrawingSupplier.DEFAULT_SHAPE_SEQUENCE);
//        plot.setDrawingSupplier(drawingSupplier);
        return new JFreeChart("Project Job Scheduling", JFreeChart.DEFAULT_TITLE_FONT, plot, true);
    }
}

结果:

另一种方法是实现 JFreeChart 接口并自定义 DatasetRenderer,以便您可以直接绘制 Allocations。类似于 JFreeChart 中的 Gantt chart 实现。

或者从头开始编写您的自定义 UI。取决于你愿意付出多少努力:)